package art.java.concurrency.chapter05;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author <a href="eric_zheng@lanzuo.com.cn">eric</a>
 * @version 1.0.0
 * Description:
 */
public class FailAndUnfairTest {

    private static Lock failLock = new ReentrantLock2(true);
    private static Lock unfairLock = new ReentrantLock2(false);


    @Test
    public void unfair() {
        testLock(unfairLock);
    }

    @Test
    public void fair() {
        testLock(failLock);
    }


    /**
     * @param lock
     */
    private void testLock(Lock lock) {
        for (int i = 0; i < 5; i++) {
            Job job = new Job(lock);
            job.setName("JOB-" + (i + 1));
            job.setDaemon(true);
            job.start();
        }
    }

    /**
     * job
     */
    private static class Job extends Thread {

        private Lock lock;

        public Job(Lock lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                System.out.println("Lock by: [" + Thread.currentThread().getName() + "] " +
                        " Waiting by: [ "
                        + buildThreadNames(new ArrayList<>(((ReentrantLock2) lock).getQueuedThreads()))
                        + "]");
            } finally {
                lock.unlock();
            }
           
        }
    }

    private static class ReentrantLock2 extends ReentrantLock {
        public ReentrantLock2(boolean fair) {
            super(fair);
        }

        /**
         * 此方法原本是protected 在此把它public
         *
         * @return 返回正在队列中等待的线程
         */
        @Override
        public Collection<Thread> getQueuedThreads() {
            List<Thread> arrayList = new ArrayList<>(super.getQueuedThreads());

            Collections.reverse(arrayList);
            return arrayList;
        }

    }

    public static String buildThreadNames(List<Thread> threads) {

        List<String> threadNames = threads.stream().map(thread -> thread.getName()).collect(Collectors.toList());

        return String.join(",  ", threadNames);
    }
}
